import React from 'react';
import Net from '../net/Net';

function isPromise(obj) {

  return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';

}
/**
 * 带有网络请求的高阶组件
 * @param url 请求接口
 * @param option 配置
 * option {
 *  data() 返回请求参数的函数
 *  extra() 附加参数函数，可以是外部函数，
 *  header() 头部函数，可以是外部函数
 *  emit: 首次加载是否触发，默认为true
 * }
 */
export default function ajax(url, option, resolver, type = true) {
  return (Wrapper) => {
    Wrapper.prototype.postAjax = function(params) {
      const { onAjax } = this.props;
      let URL = '';
      if (typeof url === 'function') {
        URL = url.apply(this);
      } else {
        URL = url;
      }
      let ajaxParam = {};
      const opt = {...option, ...params};
      if (opt.data) {
        if (typeof opt.data === 'function') {
          ajaxParam = opt.data.apply(this);
        } else if (typeof opt.data === 'string') {
          ajaxParam = this[opt.data]()
        }
      }
      if (opt.extra) {
        if (typeof opt.extra === 'function') {
          let extra = opt.extra();
          ajaxParam = Object.assign({}, ajaxParam, extra);
        } else {
          console.error('参数extra需要传入函数');
        }
      }
      return new Promise((resolve, reject) => {
        onAjax(ajaxParam, URL)
            .then(res => {
              option.end && option.end.call(this, res, ajaxParam);
              resolve(res);
            })
            .catch(err => {
              reject(err);
            });
      });
    }
    const willMount = Wrapper.prototype.componentWillMount;
    Wrapper.prototype.componentWillMount = function () {
      if (option.auto === undefined || option.auto === true || (typeof option.auto === 'string' && this.props[option.auto] === true) ) {
        this.postAjax();
      }
      willMount && willMount.apply(this);
    }
    if (option.watch) {
      const componentWillReceive = Wrapper.prototype.componentWillReceiveProps;
      Wrapper.prototype.componentWillReceiveProps = function(nextProps, nextContext) {
        let shouldPost = false;
        for(let prop of option.watch) {
          if(this.props[prop] !== nextProps[prop]) {
            shouldPost = true;
          }
        }
        if (shouldPost) {
          setTimeout(() => {
            this.postAjax();
          }, 0);
        }
        componentWillReceive && componentWillReceive.bind(this)(nextProps, nextContext);
      }
    }
    return class extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          data: {}
        }
      }

      onAjax = (params, URL) => {
        return new Promise((resolve, reject) => {
          Net(URL, params, { method: option.method ? option.method : 'GET', header: option.header ? option.header() : {} }, resolver, type)
              .then(res => {
                if (isPromise(res)) {
                  res.then(response => {
                    this.setState({
                      data: response
                    }, () => {
                      option.after && option.after(response, params);
                      resolve(response);
                    });
                  }).catch(err => {
                    reject(err);
                  });
                } else {
                  this.setState({
                    data: res
                  }, () => {
                    option.after && option.after(res, params);
                    resolve(res);
                  });
                }

              })
              .catch(err => {
                reject(err);
              });
        });

      }

      render() {
        return (
          <Wrapper {...this.props} {...this.state.data} onAjax={this.onAjax} />
        )
      }
    }
  }
}
